package com.firesyde.gwt.persistence.bdb;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.text.DateFormat;
import java.util.Date;

import org.apache.commons.beanutils.PropertyUtils;
import org.slf4j.Logger;


import com.firesyde.gwt.persistence.client.StorableDAO;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;

/**
 * Generic binding for Berkeley DB Java Edition which uses bean introspection to
 * enumerate properties
 * 
 * @author dan
 * 
 */
//@Singleton
public class BeanBinding extends TupleBinding {

	@Inject Logger logger; 
	
	private Class<? extends StorableDAO> beanClass;

	private static DateFormat dateFormat = DateFormat.getDateInstance();

	private BeanInfo beanInfo = null;

	public BeanBinding(Class<? extends StorableDAO> clazz) {
		
		this.beanClass = clazz;
		
		try {
			beanInfo = Introspector.getBeanInfo(clazz);
		} catch (IntrospectionException e) {
			System.out.println("Error introspecting beaninfo: "
					+ e.getMessage());
			e.printStackTrace();
		}
	}

	// Implement this abstract method. Used to convert
	// a DatabaseEntry to an Bean object.
	public Object entryToObject(TupleInput ti) {
		
		StorableDAO newObject = null;

		try {
			// BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);

			newObject = beanClass.newInstance();

			// TODO: other types
			for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
				if (pd.getPropertyType() == String.class) {
					PropertyUtils.setProperty(newObject, pd.getName(), ti
							.readString());
				} else if (pd.getPropertyType() == Date.class) {
					// XXX TODO: deserialise from bytes? also means searching is
					// easier.. or
					// convert to Unix date
					String dateString = ti.readString();

					// System.out.println("Deserialising "+dateString);
					PropertyUtils.setProperty(newObject, pd.getName(),
							dateFormat.parse(dateString));
				}
			}

		} catch (Exception e) {
			System.out.println("Error instantiating class: " + e.getMessage());
			e.printStackTrace();
		}

		return newObject;
	}

	// Implement this abstract method. Used to convert a
	// Inventory object to a DatabaseEntry object.
	public void objectToEntry(Object object, TupleOutput to) {

		try {
			// TODO: other types
			for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
				if (pd.getPropertyType() == String.class) {
					to.writeString((String) PropertyUtils.getProperty(object,
							pd.getName()));
				} else if (pd.getPropertyType() == Date.class) {
					// XXX TODO: deserialise from bytes? also means searching is
					// easier.. or
					// convert to Unix date
					to.writeString((String) dateFormat.format(PropertyUtils
							.getProperty(object, pd.getName())));
				}
			}
		} catch (Exception e) {
			System.out.println("Error serialising class: " + e.getMessage());
			e.printStackTrace();
		}
	}
}
